package org.linuxgears.BouncingBall;

import java.awt.Point;

public class InteractiveMovingStrategy implements BallMovingStrategy {

	private enum State { IDLE, STARTING, MOVING, STOPPING };
	private State state;
	
	private BouncingBallModel model;
	
	private Point targetPoint;
	private double distanceToTarget;
	private double distancePassed;
	private double absoluteVelocityValue;
	private double distanceX;
	private double distanceY;
	private Point startingPoint;
	
	public InteractiveMovingStrategy(BouncingBallModel model) {
		this.model = model;
		state = State.IDLE;
	}

	@Override
	public void move(Ball ball) {
		// if state is MOVING
		// 		move
		if (state == State.IDLE) return;

		BallContext xtc = model.getBallContext(ball);

		switch (state) {
			case STARTING:
				// calculate velocity based on the default ball velocity 
				// and target position
				
				// speedFactor is distance to go divided by distance 
				// moved in one tick, IOW "velocity"
				distanceX = targetPoint.x - xtc.getPosition().x;
				distanceY = targetPoint.y - xtc.getPosition().y;
				startingPoint = (Point)xtc.getPosition().clone();
				
				distanceToTarget = Math.sqrt(
						Math.pow(distanceX, 2) +
						Math.pow(distanceY, 2)
					);
					
				distancePassed = 0.0;
				
				absoluteVelocityValue = Math.sqrt(
						Math.pow(BallFactory.DEFAULT_BALL_VELOCITY.x, 2) +
						Math.pow(BallFactory.DEFAULT_BALL_VELOCITY.y, 2)
					); 
				
				state = State.MOVING;
				// no break, show must go on :)
			case MOVING:
				distancePassed = distancePassed + absoluteVelocityValue;
				if (distancePassed > distanceToTarget) {
					// we've reached the target point in this micro-move
					xtc.getPosition().x = targetPoint.x;
					xtc.getPosition().y = targetPoint.y;
					state = State.STOPPING;
				} else {
					// calculate new position, based on distance passed
					xtc.getPosition().x = startingPoint.x + 
						Math.round((float)(distanceX * (distancePassed / distanceToTarget)));
					xtc.getPosition().y = startingPoint.y + 
						Math.round((float)(distanceY * (distancePassed / distanceToTarget)));
				}

				break;
			case STOPPING:
				ball.setVelocity(BallFactory.IDLE_BALL_VELOCITY);
				break;
			default:	// IDLE

		}
	}
	
	public void startMovingTo(Ball ball, Point target) {
		if (null == target) return; // fuck it
		
		if (! model.getBounds().contains(BallTools.getBallCenterByPosition(ball, target))) return;	// fuck it, should be exception
		
		this.targetPoint = target;
		this.state = State.STARTING;
	}
	
}
